React

您所在的位置:网站首页 react suspense解决异步问题 React

React

2023-06-15 09:37| 来源: 网络整理| 查看: 265

前言

React有三种不同的模式,concurent模式、legacy模式,以及处于concurrent和legacy之间的过渡模式blocking。

这里主要讲下state 处于 Concurrent模式与 Legacy模式 差异性。

大概对应的React版本号:

模式版本ConcurrentReact18以及之后的版本LegacyReact17以及之前的版本

React未来的版本都将采用 Concurrent 模式,其采用渐进增强的方式向后兼容。

以下皆在函数组件举例,state的行为 类组件与函数组件 中表现一致。

legacy 模式 在钩子函数与合成事件中,state的表现是:批量的、异步的 import { Button } from "antd" import React, { useState } from "react" export const Index = () => { const [count, setCount] = useState(0) const handleClick = () => { setCount(1) console.log('111--', count) setCount(2) console.log('222--', count) setCount(3) console.log('333--', count) } console.log('render --', count) return ( test ) }

打印的结果:

image.png

批量的:调用了三次setCount,但是最后函数组件只渲染一次(只打印一次 render ---3),也就是将批量的更新合并成一次并进行更新的。

异步的:同时打印了三次 count

并且count值打印三次都是0,这是因为函数组件的更新,本质上是重新执行了一次函数,也就是说通过setCount更新的count值,需要在下次函数执行的时候,可以拿到更新后的值。

在异步操作(setTimeout、setInterval)中,state的表现是:非批量的、同步的 import { Button } from "antd" import React, { useState } from "react" export const Index = () => { const [count, setCount] = useState(0) const handleClick = () => { setTimeout(() => { setCount(1); console.log("count读取的是闭包值 111--", count); setCount(2); console.log("count读取的是闭包值 222--", count); setCount(3); console.log("count读取的是闭包值 333--", count); }); } console.log('render --', count) return ( test ) }

打印结果:

image.png

打印结果可以看到,代码是同步执行的,并且每执行一次setCount,都会更新一次函数组件, 也就是说更新是非批量的

unstable_batchedUpdates

在异步操作中,可以使用 unstable_batchedUpdates 实现手动批量更新。

import { Button } from "antd" import React, { useState } from "react" import { unstable_batchedUpdates } from "react-dom" export const Index = () => { const [count, setCount] = useState(0) const handleClick = () => { setTimeout(() => { unstable_batchedUpdates(() => { setCount(1); console.log("count读取的是闭包值 111--", count); setCount(2); console.log("count读取的是闭包值 222--", count); setCount(3); console.log("count读取的是闭包值 333--", count); }) }); } console.log('render --', count) return ( test ) }

打印结果:

image.png

可以看到,在引入了 unstable_batchedUpdates 改造异步操作的代码,setCount从非批量、同步 变成了 批量、异步。

场景

unstable_batchedUpdates 有一个经常遇到的使用场景,那就是在发起请求,获取结果后需要更新多次的state

import { Button } from "antd"; import React, { useState } from "react"; export const Index = () => { const [count, setCount] = useState(0); const [name, setName] = useState(""); const [age, setAge] = useState(18); const getUserInfoHttp = () => { return new Promise((resolve) => { setTimeout(() => { const obj = { name: "jack", age: 25, pageCount: 21, data: [] }; resolve(obj); }, 1000); }); }; const handleClick = () => { getUserInfoHttp().then((res) => { setName(res?.name); setAge(res?.age); setCount(res?.pageCount); }); }; console.log("render name--", name); console.log("render age--", age); console.log("render count--", count); return ( test ); };

打印结果:

image.png

可以看到打印了三次,也就是页面刷新太频繁了,可以使用 unstable_batchedUpdates 做下优化

import { Button } from "antd"; import React, { useState } from "react"; import { unstable_batchedUpdates } from "react-dom"; export const Index = () => { const [count, setCount] = useState(0); const [name, setName] = useState(""); const [age, setAge] = useState(18); // 发起请求获取信息 const getUserInfoHttp = () => { return new Promise((resolve) => { setTimeout(() => { const obj = { name: "jack", age: 25, pageCount: 21, data: [] }; resolve(obj); }, 1000); }); }; const handleClick = () => { getUserInfoHttp().then((res) => { // 批量更新优化 unstable_batchedUpdates(() => { setName(res?.name); setAge(res?.age); setCount(res?.pageCount); }); }); }; console.log("render name--", name); console.log("render age--", age); console.log("render count--", count); return ( test ); };

打印结果:

image.png

使用了 unstable_batchedUpdates 优化之后,页面只刷新了一次

concurrent 模式

不管是在异步操作中还是在合成事件中,state都表现出:批量的、异步的

1. 合成事件中

import { Button } from "antd" import { useState } from "react" export const Index = () => { const [count, setCount] = useState(0) const handleClick = () => { setCount(1) console.log('111--', count) setCount(2) console.log('222--', count) setCount(3) console.log('333--', count) } console.log('render --', count) return ( test ) }

打印结果:

image.png

2. 在异步操作中

import { Button } from "antd" import { useState } from "react" export const Index = () => { const [count, setCount] = useState(0) const handleClick = () => { setTimeout(() => { setCount(1); console.log("count读取的是闭包值 111--", count); setCount(2); console.log("count读取的是闭包值 222--", count); setCount(3); console.log("count读取的是闭包值 333--", count); }); } console.log('render --', count) return ( test ) }

打印结果:

image.png

flushSync 设置优先级

flushSync 可以将回调函数中的更新任务,放在一个较高的优先级中

使用flushSync 设置优先级

import { Button } from "antd" import { useState } from "react" import { flushSync } from "react-dom" export const Index = () => { const [count, setCount] = useState(0) const handleClick = () => { setCount(1); // 设置优先级 flushSync(() =>{ setCount(2); console.log("count 111--", count); }) setCount(3); } console.log('render --', count) return ( test ) }

打印结果:

image.png

可以看到,先更新了setCount(2),后更新setCount(3)

结论

state 的同步/异步问题,需要看场景、React当前模式

1.Legacy 模式下

a.在钩子函数和合成事件中,state表现为批量的、异步的

b.在异步操作中(setTimeout、Promise),state表现为非批量的、同步的

2.Concurrent 模式下

state表现都是 批量的、异步的

useState 需要注意的一些问题 浅比较 const [userInfo, setUserInfo] = useState({}) const copyData = { ...userInfo } // 进行浅拷贝 重新分配内存空间 copyData.name = 'jack' setUserInfo(copyData)

以下是无效的写法:

const [userInfo, setUserInfo] = useState({}) userInfo.name = 'jack' setUserInfo(userInfo) // 不会更新 因为浅比较的结果是,值userInfo没有发生变化

原因分析:在进行setState更新值的时候,React会进行一次浅比较,也就是当前更新的值和上一次的值,是否有发生变化,如果值没有发生变化,那么不进行更新,否则进行更新。浅比较是进行内存地址的比较,因此直接在原来的值上进行修改,相当于没有变化

获取最新值

给 useState 的 dispatch 传递的参数是一个函数,既可获取修改后的值

const [count, setCount] = useState(0) setCount(value => value + 1) // count = 0 + 1 setCount(value => value + 1) // count = 1 + 1 setCount(value => value + 1) // count = 2 + 1 参考文献

React 的 setState 是同步还是异步

setState的执行机制

React useState和setState到底是同步还是异步



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3